import {
  IDocumentationSection,
  DocumentationConfig,
  CodebaseContext,
  DocumentationResult,
  DocumentationMetadata,
} from "../interfaces/documentation.interface";
import { PatternExtractionService } from "../services/pattern-extraction.service";
import { DocumentationConfigService } from "../services/documentation-config.service";

/**
 * Abstract base class for documentation sections
 */
abstract class DocumentationSectionBase implements IDocumentationSection {
  protected readonly patternService: PatternExtractionService;
  protected readonly configService: DocumentationConfigService;

  constructor() {
    this.patternService = PatternExtractionService.getInstance();
    this.configService = DocumentationConfigService.getInstance();
  }

  abstract readonly sectionName: string;
  abstract generate(
    context: CodebaseContext,
    config: DocumentationConfig,
  ): Promise<DocumentationResult>;

  /**
   * Create base metadata for documentation
   */
  protected createMetadata(
    sections: string[],
    content: string,
  ): DocumentationMetadata {
    return {
      generatedAt: new Date(),
      version: "1.0.0",
      sections,
      wordCount: content.split(/\s+/).length,
    };
  }
}

/**
 * README documentation section
 */
export class ReadmeDocumentationSection extends DocumentationSectionBase {
  readonly sectionName = "README";

  async generate(
    context: CodebaseContext,
    config: DocumentationConfig,
  ): Promise<DocumentationResult> {
    const content = await this.generateReadmeContent(context, config);
    const metadata = this.createMetadata(
      ["overview", "installation", "usage"],
      content,
    );

    return {
      fileName: "README.md",
      content,
      metadata,
    };
  }

  private async generateReadmeContent(
    context: CodebaseContext,
    config: DocumentationConfig,
  ): Promise<string> {
    const projectName = this.extractProjectName(context);
    const description = this.generateDescription(context);
    const technologies =
      context.technologies.join(", ") || "Various technologies";

    let content = `# ${projectName}\n\n`;
    content += `> ${description}\n\n`;
    content += `## 🚀 Overview\n\n`;
    content += `This project is built with **${technologies}** and follows modern development practices.\n\n`;

    // Installation section
    content += `## 📦 Installation\n\n`;
    content += this.generateInstallationInstructions(context);

    // Usage section if requested
    if (config.includeUsage) {
      content += `\n## 🔧 Usage\n\n`;
      content += this.generateUsageInstructions(context);
    }

    // Architecture link
    if (config.includeArchitecture) {
      content += `\n## 🏗️ Architecture\n\n`;
      content += `For detailed architecture information, see [Architecture Documentation](./ARCHITECTURE.md).\n`;
    }

    // API link
    if (config.includeAPI) {
      content += `\n## 📚 API Documentation\n\n`;
      content += `For API documentation, see [API Documentation](./API.md).\n`;
    }

    content += `\n## 🤝 Contributing\n\n`;
    content += `1. Fork the repository\n`;
    content += `2. Create your feature branch (\`git checkout -b feature/amazing-feature\`)\n`;
    content += `3. Commit your changes (\`git commit -m 'Add amazing feature'\`)\n`;
    content += `4. Push to the branch (\`git push origin feature/amazing-feature\`)\n`;
    content += `5. Open a Pull Request\n\n`;

    content += `## 📄 License\n\n`;
    content += `This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n`;

    content += `---\n\n`;
    content += `*This documentation was automatically generated by [CodeBuddy](https://github.com/olasunkanmi-SE/codebuddy) on ${new Date().toLocaleDateString()}*\n`;

    return content;
  }

  private extractProjectName(context: CodebaseContext): string {
    // Try to find package.json for project name
    const packageJsonFile = context.files.find((f) =>
      f.path.endsWith("package.json"),
    );
    if (packageJsonFile) {
      try {
        const packageJson = JSON.parse(packageJsonFile.content);
        if (packageJson.name) {
          return packageJson.name
            .replace(/[-_]/g, " ")
            .replace(/\b\w/g, (l: string) => l.toUpperCase());
        }
      } catch (error: any) {
        console.warn("Failed to parse package.json:", error);
      }
    }

    // Fallback to directory name or generic name
    const mainDir = context.structure.directories[0];
    return mainDir ? mainDir.split("/").pop() || "Project" : "Project";
  }

  private generateDescription(context: CodebaseContext): string {
    const hasAPI = context.patterns.some(
      (p) => p.name.includes("API") || p.name.includes("REST"),
    );
    const hasReact = context.technologies.includes("React");
    const hasExpress = context.technologies.includes("Express.js");

    if (hasAPI && hasReact) {
      return "A full-stack application with modern frontend and robust API backend";
    } else if (hasReact) {
      return "A modern React application with component-based architecture";
    } else if (hasExpress || hasAPI) {
      return "A robust backend API service with RESTful endpoints";
    }

    return "A well-structured software project built with modern development practices";
  }

  private generateInstallationInstructions(context: CodebaseContext): string {
    const hasPackageJson = context.files.some((f) =>
      f.path.endsWith("package.json"),
    );
    const hasPipfile = context.files.some(
      (f) => f.path.endsWith("Pipfile") || f.path.endsWith("requirements.txt"),
    );
    const hasComposer = context.files.some((f) =>
      f.path.endsWith("composer.json"),
    );

    if (hasPackageJson) {
      return `\`\`\`bash\n# Install dependencies\nnpm install\n\n# Start development server\nnpm run dev\n\`\`\`\n`;
    } else if (hasPipfile) {
      return `\`\`\`bash\n# Install dependencies\npip install -r requirements.txt\n\n# Run the application\npython main.py\n\`\`\`\n`;
    } else if (hasComposer) {
      return `\`\`\`bash\n# Install dependencies\ncomposer install\n\n# Start the application\nphp -S localhost:8000\n\`\`\`\n`;
    }

    return `\`\`\`bash\n# Clone the repository\ngit clone <repository-url>\n\n# Follow setup instructions specific to this project\n\`\`\`\n`;
  }

  private generateUsageInstructions(context: CodebaseContext): string {
    const hasAPI = context.patterns.some(
      (p) => p.name.includes("API") || p.name.includes("REST"),
    );
    const hasReact = context.technologies.includes("React");

    let usage = `### Basic Usage\n\n`;

    if (hasReact) {
      usage += `This is a React application. After installation:\n\n`;
      usage += `1. Start the development server with \`npm run dev\`\n`;
      usage += `2. Open your browser to \`http://localhost:3000\`\n`;
      usage += `3. Begin developing your components in the \`src/\` directory\n\n`;
    }

    if (hasAPI) {
      usage += `### API Usage\n\n`;
      usage += `The API server provides RESTful endpoints. Example requests:\n\n`;
      usage += `\`\`\`bash\n# Example API calls\ncurl -X GET http://localhost:3000/api/health\n\`\`\`\n\n`;
    }

    if (!hasReact && !hasAPI) {
      usage += `Refer to the project-specific documentation for detailed usage instructions.\n\n`;
    }

    return usage;
  }
}

/**
 * API documentation section
 */
export class APIDocumentationSection extends DocumentationSectionBase {
  readonly sectionName = "API";

  async generate(
    context: CodebaseContext,
    config: DocumentationConfig,
  ): Promise<DocumentationResult> {
    const content = await this.generateAPIContent(context);
    const metadata = this.createMetadata(["endpoints", "models"], content);

    return {
      fileName: "API.md",
      content,
      metadata,
    };
  }

  private async generateAPIContent(context: CodebaseContext): Promise<string> {
    let content = `# API Documentation\n\n`;
    content += `> Automatically generated API documentation\n\n`;

    const allContent = context.files.map((f) => f.content).join("\n");
    const endpoints = await this.patternService.extractAPIEndpoints(allContent);

    content += this.generateEndpointsSection(endpoints);
    content += this.generateFooter();

    return content;
  }

  private generateEndpointsSection(endpoints: any[]): string {
    if (endpoints.length === 0) {
      return `## 🔗 Endpoints\n\nNo API endpoints were detected in this codebase.\n\n`;
    }

    let content = `## 🔗 Endpoints\n\n`;
    const groupedEndpoints = this.groupEndpointsByMethod(endpoints);

    for (const [method, methodEndpoints] of Object.entries(groupedEndpoints)) {
      content += `### ${method} Endpoints\n\n`;
      content += this.generateMethodEndpoints(method, methodEndpoints);
    }

    return content;
  }

  private generateMethodEndpoints(method: string, endpoints: any[]): string {
    let content = "";

    for (const endpoint of endpoints) {
      content += `#### \`${method} ${endpoint.path}\`\n\n`;
      content += `${endpoint.description}\n\n`;

      content += this.generateParametersSection(endpoint.parameters);
      content += this.generateResponsesSection(endpoint.responses);
      content += `---\n\n`;
    }

    return content;
  }

  private generateParametersSection(parameters: any[]): string {
    if (parameters.length === 0) {
      return "";
    }

    let content = `**Parameters:**\n\n`;
    for (const param of parameters) {
      const required = param.required ? " *required*" : " *optional*";
      content += `- \`${param.name}\` (${param.type})${required} - ${param.description}\n`;
    }
    return content + `\n`;
  }

  private generateResponsesSection(responses: any[]): string {
    let content = `**Responses:**\n\n`;
    for (const response of responses) {
      content += `- \`${response.status}\` - ${response.description}\n`;
    }
    return content + `\n`;
  }

  private generateFooter(): string {
    return `---\n\n*This documentation was automatically generated by [CodeBuddy](https://github.com/olasunkanmi-SE/codebuddy) on ${new Date().toLocaleDateString()}*\n`;
  }

  private groupEndpointsByMethod(endpoints: any[]): Record<string, any[]> {
    return endpoints.reduce(
      (groups, endpoint) => {
        const method = endpoint.method;
        if (!groups[method]) {
          groups[method] = [];
        }
        groups[method].push(endpoint);
        return groups;
      },
      {} as Record<string, any[]>,
    );
  }
}

/**
 * Architecture documentation section
 */
export class ArchitectureDocumentationSection extends DocumentationSectionBase {
  readonly sectionName = "ARCHITECTURE";

  async generate(
    context: CodebaseContext,
    config: DocumentationConfig,
  ): Promise<DocumentationResult> {
    const content = await this.generateArchitectureContent(context, config);
    const metadata = this.createMetadata(
      ["overview", "components", "patterns"],
      content,
    );

    return {
      fileName: "ARCHITECTURE.md",
      content,
      metadata,
    };
  }

  private async generateArchitectureContent(
    context: CodebaseContext,
    config: DocumentationConfig,
  ): Promise<string> {
    let content = `# Architecture Documentation\n\n`;
    content += `> System architecture and design overview\n\n`;

    // System overview
    content += `## 🏗️ System Overview\n\n`;
    content += this.generateSystemOverview(context);

    // Technologies
    if (context.technologies.length > 0) {
      content += `\n## 🛠️ Technology Stack\n\n`;
      for (const tech of context.technologies) {
        content += `- **${tech}**\n`;
      }
      content += `\n`;
    }

    // Patterns
    if (context.patterns.length > 0) {
      content += `## 📋 Design Patterns\n\n`;
      for (const pattern of context.patterns) {
        content += `### ${pattern.name}\n\n`;
        content += `${pattern.description}\n`;
        content += `*Confidence: ${Math.round(pattern.confidence * 100)}%*\n\n`;
      }
    }

    // Architecture diagram
    if (config.diagramFormat === "mermaid") {
      content += `## 📊 Architecture Diagram\n\n`;
      content += this.generateMermaidDiagram(context);
    }

    content += `\n---\n\n`;
    content += `*This documentation was automatically generated by [CodeBuddy](https://github.com/olasunkanmi-SE/codebuddy) on ${new Date().toLocaleDateString()}*\n`;

    return content;
  }

  private generateSystemOverview(context: CodebaseContext): string {
    const hasAPI = context.patterns.some(
      (p) => p.name.includes("API") || p.name.includes("REST"),
    );
    const hasDatabase = context.technologies.some(
      (tech) =>
        tech.includes("MongoDB") ||
        tech.includes("PostgreSQL") ||
        tech.includes("MySQL"),
    );
    const hasFrontend = context.technologies.some(
      (tech) =>
        tech.includes("React") ||
        tech.includes("Vue") ||
        tech.includes("Angular"),
    );

    let overview = `This system follows a `;

    if (hasFrontend && hasAPI && hasDatabase) {
      overview += `**full-stack architecture** with clear separation between frontend, backend, and data layers.\n\n`;
      overview += `The architecture promotes:\n`;
      overview += `- **Separation of Concerns**: Clear boundaries between layers\n`;
      overview += `- **Scalability**: Modular design allows independent scaling\n`;
      overview += `- **Maintainability**: Well-organized code structure\n`;
    } else if (hasAPI) {
      overview += `**service-oriented architecture** focused on providing robust API services.\n\n`;
      overview += `Key architectural principles:\n`;
      overview += `- **RESTful Design**: Clean, predictable API endpoints\n`;
      overview += `- **Service Isolation**: Independent, focused services\n`;
      overview += `- **Data Consistency**: Reliable data management patterns\n`;
    } else if (hasFrontend) {
      overview += `**component-based frontend architecture** with modern UI patterns.\n\n`;
      overview += `Architectural highlights:\n`;
      overview += `- **Component Reusability**: Modular, reusable UI components\n`;
      overview += `- **State Management**: Organized application state handling\n`;
      overview += `- **Responsive Design**: Adaptive user interface\n`;
    } else {
      overview += `**modular architecture** with well-defined component boundaries.\n\n`;
      overview += `Core principles:\n`;
      overview += `- **Modularity**: Clear separation of functionality\n`;
      overview += `- **Extensibility**: Easy to add new features\n`;
      overview += `- **Code Organization**: Structured project layout\n`;
    }

    return overview;
  }

  private generateMermaidDiagram(context: CodebaseContext): string {
    const hasAPI = context.patterns.some(
      (p) => p.name.includes("API") || p.name.includes("REST"),
    );
    const hasDatabase = context.technologies.some(
      (tech) =>
        tech.includes("MongoDB") ||
        tech.includes("PostgreSQL") ||
        tech.includes("MySQL"),
    );
    const hasFrontend = context.technologies.some(
      (tech) =>
        tech.includes("React") ||
        tech.includes("Vue") ||
        tech.includes("Angular"),
    );

    let diagram = "```mermaid\ngraph TD\n";

    if (hasFrontend && hasAPI && hasDatabase) {
      diagram += "    A[Client/Browser] --> B[Frontend Application]\n";
      diagram += "    B --> C[API Gateway/Router]\n";
      diagram += "    C --> D[Business Logic Layer]\n";
      diagram += "    D --> E[Data Access Layer]\n";
      diagram += "    E --> F[Database]\n";
    } else if (hasAPI && hasDatabase) {
      diagram += "    A[Client] --> B[API Layer]\n";
      diagram += "    B --> C[Service Layer]\n";
      diagram += "    C --> D[Data Layer]\n";
      diagram += "    D --> E[Database]\n";
    } else if (hasFrontend) {
      diagram += "    A[User] --> B[UI Components]\n";
      diagram += "    B --> C[State Management]\n";
      diagram += "    C --> D[Business Logic]\n";
      diagram += "    D --> E[External APIs]\n";
    } else {
      diagram += "    A[Input] --> B[Processing Layer]\n";
      diagram += "    B --> C[Business Logic]\n";
      diagram += "    C --> D[Output Layer]\n";
    }

    diagram += "\n    style A fill:#e1f5fe\n";
    diagram += "    style B fill:#f3e5f5\n";
    diagram += "    style C fill:#e8f5e8\n";
    diagram += "    style D fill:#fff3e0\n";
    diagram += "    style E fill:#fce4ec\n";

    if (hasDatabase) {
      diagram += "    style F fill:#f1f8e9\n";
    }

    diagram += "```\n";

    return diagram;
  }
}

/**
 * Components documentation section
 */
export class ComponentsDocumentationSection extends DocumentationSectionBase {
  readonly sectionName = "COMPONENTS";

  async generate(
    context: CodebaseContext,
    config: DocumentationConfig,
  ): Promise<DocumentationResult> {
    const content = await this.generateComponentsContent(context);
    const metadata = this.createMetadata(
      ["classes", "interfaces", "functions"],
      content,
    );

    return {
      fileName: "COMPONENTS.md",
      content,
      metadata,
    };
  }

  private async generateComponentsContent(
    context: CodebaseContext,
  ): Promise<string> {
    let content = `# Components Documentation\n\n`;
    content += `> Detailed documentation of system components\n\n`;

    const allContent = context.files.map((f) => f.content).join("\n");
    const components = await this.patternService.extractComponents(allContent);

    content += this.generateComponentsSection(components);
    content += `---\n\n*This documentation was automatically generated by [CodeBuddy](https://github.com/olasunkanmi-SE/codebuddy) on ${new Date().toLocaleDateString()}*\n`;

    return content;
  }

  private generateComponentsSection(components: any[]): string {
    if (components.length === 0) {
      return `## Components\n\nNo components were detected in this codebase.\n\n`;
    }

    let content = "";
    const groupedComponents = this.groupComponentsByType(components);

    for (const [type, typeComponents] of Object.entries(groupedComponents)) {
      content += `## ${type.charAt(0).toUpperCase() + type.slice(1)} Components\n\n`;
      content += this.generateTypeComponents(typeComponents);
    }

    return content;
  }

  private generateTypeComponents(components: any[]): string {
    let content = "";

    for (const component of components) {
      content += `### ${component.name}\n\n`;
      content += `${component.description}\n\n`;

      content += this.generateMethodsSection(component.methods);
      content += this.generatePropertiesSection(component.properties);
      content += `---\n\n`;
    }

    return content;
  }

  private generateMethodsSection(methods: any[]): string {
    if (methods.length === 0) {
      return "";
    }

    let content = `**Methods:**\n\n`;
    for (const method of methods) {
      content += `- \`${method.name}(${method.parameters.join(", ")})\` → \`${method.returnType}\`\n`;
      content += `  - ${method.description}\n\n`;
    }
    return content;
  }

  private generatePropertiesSection(properties: any[]): string {
    if (properties.length === 0) {
      return "";
    }

    let content = `**Properties:**\n\n`;
    for (const property of properties) {
      content += `- \`${property.name}: ${property.type}\` - ${property.description}\n`;
    }
    return content + `\n`;
  }

  private groupComponentsByType(components: any[]): Record<string, any[]> {
    return components.reduce(
      (groups, component) => {
        const type = component.type;
        if (!groups[type]) {
          groups[type] = [];
        }
        groups[type].push(component);
        return groups;
      },
      {} as Record<string, any[]>,
    );
  }
}
